<template>
  <div
    v-show="layoutReady"
    class="overflow-hidden flex flex-col items-stretch shrink-0 transition-all"
    :style="{
      height: expanded ? `${panelHeight}px` : '1rem',
    }"
    :data-parent-height="parentHeight"
  >
    <div
      class="cursor-pointer flex items-center justify-start px-2 gap-1 overflow-y-visible bg-control-bg hover:bg-control-bg-hover"
      :style="{
        height: '1rem',
      }"
      @click="expanded = !expanded"
    >
      <ChevronDownIcon
        class="w-4 h-4 transition-transform"
        :class="expanded ? '' : 'rotate-180'"
      />
      <span v-if="true || expanded" class="text-xs origin-left">
        {{ $t("schema-editor.preview-schema-text") }}
      </span>
    </div>
    <div class="relative flex-1 overflow-hidden">
      <MonacoEditor
        v-if="status === 'pending' || status === 'success'"
        :readonly="true"
        :content="data?.trim() ?? ''"
        :options="{
          fontSize: '12px',
          lineHeight: '18px',
        }"
        class="w-full h-full"
      />
      <pre v-if="expanded && status === 'error'" class="text-sm text-error">{{
        error?.message ?? "Unknown error"
      }}</pre>
      <MaskSpinner v-if="status === 'pending'" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { useQuery } from "@tanstack/vue-query";
import {
  useElementSize,
  useLocalStorage,
  useParentElement,
} from "@vueuse/core";
import { cloneDeep } from "lodash-es";
import { ChevronDownIcon } from "lucide-vue-next";
import { computed } from "vue";
import { MonacoEditor } from "@/components/MonacoEditor";
import MaskSpinner from "@/components/misc/MaskSpinner.vue";
import { sqlServiceClient } from "@/grpcweb";
import type { ComposedDatabase } from "@/types";
import {
  DatabaseMetadata,
  SchemaConfig,
  SchemaMetadata,
  type TableMetadata,
} from "@/types/proto/v1/database_service";
import { minmax } from "@/utils";
import { extractGrpcErrorMessage } from "@/utils/grpcweb";
import { useSchemaEditorContext } from "../context";

const props = defineProps<{
  db: ComposedDatabase;
  database: DatabaseMetadata;
  schema: SchemaMetadata;
  table: TableMetadata;
}>();

const expanded = useLocalStorage(
  "bb.schema-editor.table-schema-preview.expanded",
  true
);
const { getColumnStatus } = useSchemaEditorContext();
const parentElement = useParentElement();
const { height: parentHeight } = useElementSize(parentElement);
const layoutReady = computed(() => parentHeight.value > 0);
const panelHeight = computed(() => {
  const min = 8 * 16; // 8rem ~= 6 lines
  const max = 16 * 16; // 16rem ~= 13 lines
  const flexible = parentHeight.value * 0.4;
  return minmax(flexible, min, max);
});

const engine = computed(() => props.db.instanceResource.engine);
const mockedSchemaMetadata = computed(() => {
  if (!expanded.value) return undefined;
  const { db, database, schema, table } = props;

  const mockedTable = cloneDeep(table);
  mockedTable.columns = mockedTable.columns.filter((column) => {
    const status = getColumnStatus(db, { database, schema, table, column });
    return status !== "dropped";
  });
  const mockedDatabase = DatabaseMetadata.fromJSON({
    name: database.name,
    characterSet: database.characterSet,
    collation: database.collation,
    schemas: [
      {
        name: schema.name,
        tables: [mockedTable],
      },
    ],
  });
  const schemaConfig = database.schemaConfigs.find(
    (sc) => sc.name === schema.name
  );
  const tableConfig = schemaConfig?.tableConfigs.find(
    (tc) => tc.name === table.name
  );
  if (schemaConfig && tableConfig) {
    mockedDatabase.schemaConfigs = [
      SchemaConfig.fromJSON({
        name: schemaConfig.name,
        tableConfigs: [cloneDeep(tableConfig)],
      }),
    ];
  }
  return mockedDatabase;
});

const { status, data, error } = useQuery({
  queryKey: [engine, mockedSchemaMetadata],
  queryFn: async () => {
    if (!expanded.value) return "";
    const metadata = mockedSchemaMetadata.value;
    if (!metadata) return "";
    try {
      const response = await sqlServiceClient.stringifyMetadata({
        engine: engine.value,
        metadata,
      });
      return response.schema;
    } catch (err) {
      return Promise.reject(new Error(extractGrpcErrorMessage(err)));
    }
  },
});
</script>
